/*
 * Copyright 2009 XueSong Guo.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cn.webwheel.compiler;

import javax.tools.*;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

public class JDKJavaC extends JavaC {

    private static final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

    private MyClassLoader classLoader;
    private MemFileManager fileManager;

    private String classpath;

    private DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();

    JDKJavaC(ClassLoader parentClassLoader) {

        if (compiler == null) {
            throw new CompileException("system java compiler is null");
        }

        if (parentClassLoader == null) parentClassLoader = getClass().getClassLoader();

        classLoader = new MyClassLoader(parentClassLoader);

        StringBuilder sb = new StringBuilder();
        while (parentClassLoader != null) {
            if (parentClassLoader instanceof URLClassLoader) {
                for (URL url : ((URLClassLoader) parentClassLoader).getURLs()) {
                    if (url.getProtocol().equals("file")) {
                        String file = url.getFile();
                        if (file.isEmpty()) continue;
                        sb.append(file).append(File.pathSeparatorChar);
                    } else if (url.getProtocol().equals("jar")) {
                        String path = url.getPath();
                        int i = path.lastIndexOf('!');
                        if (i != -1) {
                            path = path.substring(0, i);
                        }
                        URL url2;
                        try {
                            url2 = new URL(path);
                        } catch (MalformedURLException e) {
                            if (debug) {
                                e.printStackTrace();
                            }
                            continue;
                        }
                        if (url2.getProtocol().equals("file")) {
                            String file = url2.getFile();
                            if(file.isEmpty()) continue;
                            sb.append(file).append(File.pathSeparatorChar);
                        }
                    }
                }
            }
            parentClassLoader = parentClassLoader.getParent();
        }
        classpath = sb.toString();

        if (debug) {
            diagnostics = null;
            System.err.println("classpath: " + classpath);
        }

        try {
            fileManager = new MemFileManager(compiler.getStandardFileManager(diagnostics, null, null), classLoader);
        } catch (URISyntaxException e) {
            throw new CompileException(e);
        }
    }

    public Class compile(String name, String source) {

        String className = "cn.webwheel.gen." + name;

        source = "package cn.webwheel.gen;" + source;

        if (debug) {
            System.err.println("using jdk javac");
            System.err.println("compiling: " + name);
            System.err.println(source);
        }

        List<StringJavaFileObject> srcs = new ArrayList<StringJavaFileObject>();
        try {
            srcs.add(new StringJavaFileObject(name, source));
        } catch (URISyntaxException e) {
            throw new CompileException(e);
        }

        List<String> options = Arrays.asList("-g:none", "-cp", classpath);

        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, options, null, srcs);

        synchronized (compiler) {
            if(!task.call()) {
                StringBuilder sb = new StringBuilder();
                String s = System.getProperty("line.separator");
                for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
                    sb.append(diagnostic.getMessage(Locale.getDefault())).append(s);
                }
                sb.delete(sb.length() - s.length(), sb.length());
                throw new CompileException(sb.toString(), diagnostics);
            }
        }

        return classLoader.defineClass(className, fileManager.findJavaFileObject(className).toBytes());
    }
}
